
/**
 * AppleIIGo
 * Motherboard class
 * (C) 2007-2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include <string.h>

#include "Motherboard.h"
#include "MotherboardConfigurations.h"

char * slotDeviceName[] = {
	"Slot0Device", "Slot1Device",
	"Slot2Device", "Slot3Device",
	"Slot4Device", "Slot5Device",
	"Slot6Device", "Slot7Device",
};

char * internalSlotDeviceName[] = {
	"InternalSlot0Device", "InternalSlot1Device",
	"InternalSlot2Device", "InternalSlot3Device",
	"InternalSlot4Device", "InternalSlot5Device",
	"InternalSlot6Device", "InternalSlot7Device",
};

Motherboard::Motherboard(char * profilePath, char * resourcesPath) {
	// To-Do: extract form profile
	bool isToDo = true;
	
	char * configurationName = profilePath;
	char * auxSlotDeviceType = "InternalAuxMemory";
	char * slotDeviceType[8] = {
		"-Test",
		"-GrapplerPlus",
		"-TransWarp",
		"-Videoterm",
		"-AppleIIMemoryExpansionCard",
		"-GraphicsTabletInterfaceCard",
		"-DiskII16SectorInterfaceCard",
		"-Apple35FloppyDiskDriveInterfaceCard",
	};
	
	deviceList = new List();
	diskDriveList = new List();
	
	resources = new Resources(resourcesPath);
	
	MotherboardConfigurationType * conf =
		getConfiguration(configurationName);
	motherboardCpu = cpu = CPU::getInstance(conf->cpuType, this);
	mmu = new MMU(this, conf->mmuType);
	iou = new IOU(this, conf->iouType);
	memory = new Memory(this, conf->mainMemoryType, conf->mainMemoryByteNum,
						conf->romImage, conf->romByteNum, conf->romBank1Index);
	mig = new MIG(this);
	timingGenerator = new TimingGenerator(this, conf->isPalTiming);
	videoRenderer = VideoRenderer::getInstance(conf->videoRendererType, this,
											   conf->videoType,
											   conf->characterSetType,
											   conf->characterSetRomImage);
	speaker = new Speaker(this, conf->speakerType);
	keyboard = new Keyboard(this, conf->isKeyboardLowerCase);
	paddles = new Paddles(this);
	mouse = new Mouse(this);
	adb = new ADB(this);
	doc = new DOC(this);
	rtc = new RTC(this);
	sccA = new SCC(this, "SCCA");
	sccB = new SCC(this, "SCCB");
	auxSlot = AuxSlotDevice::getInstance(auxSlotDeviceType, this);
	for (SINT32 index = (MAPPEDSLOT_NUM - 1); index >= 0; index--)
		slot[SLOT_EXTERNAL + index] = SlotDevice::getInstance(
			slotDeviceType[index], this, slotDeviceName[index]);
	for (SINT32 index = (MAPPEDSLOT_NUM - 1); index >= 0; index--)
		slot[SLOT_INTERNAL + index] = SlotDevice::getInstance(
			conf->internalSlotDeviceType[index], this,
			internalSlotDeviceName[index]);
	chip = ChipDevice::getInstance(conf->chipDeviceType, this);
	
	reconnect();
	
	requestMemory();
	
	restart();
}

Motherboard::~Motherboard() {
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next())
		delete (Device *) deviceList->get();
	delete deviceList;
	delete diskDriveList;
	
	delete resources;
}

void Motherboard::setAudioConfiguration(SINT32 * audioInputBuffer,
										SINT32 * audioOutputBuffer,
										UINT32 audioFrameSampleNum) {
//	this->audioInputBuffer = audioInputBuffer;
//	this->audioOutputBuffer = audioOutputBuffer;
//	this->audioFrameSampleNum = audioFrameSampleNum;
}

void Motherboard::registerDevice(Device * device) {
	deviceList->insert(device);
}

void Motherboard::registerDiskDrive(DiskDrive * diskDrive) {
	diskDriveList->insert(diskDrive);
}

bool Motherboard::mount(char * diskImagePath, UINT32 diskDriveIndex) {
	UINT32 diskDriveCount[DISKDRIVETYPE_END];
	for (UINT32 index = 0; index < DISKDRIVETYPE_END; index++)
		diskDriveCount[index] = 0;
	
	for (diskDriveList->setStart(); diskDriveList->isNext();
		 diskDriveList->next()) {
		DiskDrive * diskDrive = (DiskDrive *) diskDriveList->get();
		UINT32 diskDriveType = diskDrive->getType();
		if (diskDriveCount[diskDriveType] == diskDriveIndex)
			if (diskDrive->mount(diskImagePath))
				return true;
		diskDriveCount[diskDriveType]++;
	}
	
	return false;
}

void Motherboard::unmount(UINT32 mountIndex) {
	UINT32 mountCount = 0;
	for (diskDriveList->setStart(); diskDriveList->isNext();
		 diskDriveList->next(), mountCount++)
		if (mountCount == mountIndex)
			((DiskDrive *) diskDriveList->get())->unmount();
}

void Motherboard::reset() {
	cpu->assertReset(false);
	cpu->clearReset();
}

void Motherboard::restart() {
	cpu->assertReset(true);
	cpu->clearReset();
}

void Motherboard::processFrame() {
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next()) {
		Device * device = (Device *) deviceList->get();
		device->onFrameStart();
	}
	timingGenerator->processFrame();
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next()) {
		Device * device = (Device *) deviceList->get();
		device->onFrameEnd();
	}
}

void Motherboard::onResetAsserted(bool isRestart) {
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next()) {
		Device * device = (Device *) deviceList->get();
		device->onResetAsserted(isRestart);
	}
}

void Motherboard::onResetCleared() {
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next()) {
		Device * device = (Device *) deviceList->get();
		device->onResetCleared();
	}
}

Resources * Motherboard::getResources() {
	return resources;
}

void Motherboard::setCpu(CPU * cpu) {
	if (cpu)
		this->cpu = cpu;
	else
		this->cpu = motherboardCpu;
	
	reconnect();
}

CPU * Motherboard::getCpu() {
	return cpu;
}

MMU * Motherboard::getMmu() {
	return mmu;
}

IOU * Motherboard::getIou() {
	return iou;
}

Memory * Motherboard::getMemory() {
	return memory;
}

MIG * Motherboard::getMig() {
	return mig;
}

TimingGenerator * Motherboard::getTimingGenerator() {
	return timingGenerator;
}

VideoRenderer * Motherboard::getVideoRenderer() {
	return videoRenderer;
}

Speaker * Motherboard::getSpeaker() {
	return speaker;
}

Keyboard * Motherboard::getKeyboard() {
	return keyboard;
}

Paddles * Motherboard::getPaddles() {
	return paddles;
}

Mouse * Motherboard::getMouse() {
	return mouse;
}

ADB * Motherboard::getAdb() {
	return adb;
}

DOC * Motherboard::getDoc() {
	return doc;
}

RTC * Motherboard::getRtc() {
	return rtc;
}

SCC * Motherboard::getSccA() {
	return sccA;
}

SCC * Motherboard::getSccB() {
	return sccB;
}

AuxSlotDevice * Motherboard::getAuxSlotDevice() {
	return auxSlot;
}

SlotDevice * Motherboard::getSlotDevice(UINT32 slotIndex) {
	return slot[slotIndex];
}

ChipDevice * Motherboard::getChipDevice() {
	return chip;
}

MotherboardConfigurationType * Motherboard::getConfiguration(
	char * confName) {
	for (int index = 0; index < sizeof(motherboardConfigurations) / 
		 sizeof(MotherboardConfigurationType); index++) {
		if (!strcmp(motherboardConfigurations[index].configurationName,
					confName))
			return &(motherboardConfigurations[index]);
	}
	return &(motherboardConfigurations[0]);
}

void Motherboard::reconnect() {
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next()) {
		Device * device = (Device *) deviceList->get();
		device->onDevicesConnected();
	}
}

void Motherboard::requestMemory() {
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next()) {
		Device * device = (Device *) deviceList->get();
		device->onMemoryRequested();
	}
	memory->allocateMemory();
	for (deviceList->setStart(); deviceList->isNext(); deviceList->next()) {
		Device * device = (Device *) deviceList->get();
		device->onMemoryAllocated();
	}
}
